#include "bsplinepatch.h"

#include <iostream>

namespace rendersystem{

class BSplineCurveEvaluator {
public :

	static glm::vec3 evaluate (const std::vector<glm::vec3> &points,
			const std::vector<float> &knots,
			int order,
			float u){

		std::vector<float> newKnots;
		std::vector<glm::vec3> newPoints;
		glm::vec3 ret;


		if(u<knots[order-1] || u>knots[knots.size()-1]){
			//	std::cerr<<" return ret "  <<std::endl;
			return ret;//valeur retour fausse de ret?
		}
		//   std::cerr<<" debut " <<std::endl;
		int j;
		int i=order;
		int dec=0;
		// std::cerr<<"knotsize ; "<<knots.size() <<std::endl;
		while(u>knots[i]){
			//    	std::cerr<<"u = "<< u <<" knot["<<i<<"] = "<<knots[i] << "  dec = "<<dec <<std::endl;
			i++;
			dec++;
		}
		for(j=dec;j<dec+order;j++){
			//    std::cerr<<"point["<<j<<"] = "<<points[j] <<std::endl;
			newPoints.push_back(points[j]);
		}
		// std::cerr<<"fin 1er for "<<std::endl;
		for(j=dec+1;j<dec+1+order-1+order-2;j++){
			//  	  std::cerr<<"noeud["<<j<<"] = "<<knots[j]<<std::endl;
			newKnots.push_back(knots[j]);
		}
		//    std::cerr<<"ret=blossom  : "<< order << " "<< u <<std::endl;
		ret=blossom(newPoints,newKnots,order,u);
		/// @todo TP GEOM
		return ret;
	}

	static glm::vec3 evaluateTan (const std::vector<glm::vec3> &points,
			const std::vector<float> &knots,
			int order,
			float u){

		glm::vec3 pts = evaluate(points,knots,order,u);





		glm::vec3 ret;
		/// @todo TP GEOM
		return ret;
	}

	static glm::vec3 blossom(std::vector<glm::vec3> &points,
			std::vector<float>   &knots,
			int order,
			float u){

		std::vector<float> newKnots;
		std::vector<glm::vec3> newPoints;
		/// @todo TP1 G

		//std::cerr<<"debut blossom "<<std::endl;
		int i,j;
		for(i=0;i<order-2;i++){
			//formule pour calculer le nouveau point i par rapport à point[i] et points[i+1]
			/*		std::cerr<<" coef   "<<((knots[i+order-1]-u)/(knots[i+order-1]-knots[i]) ) <<"  points["<<i<<"] = "<< points[i]<<std::endl;

    		std::cerr<<" coef    "<<((u-knots[i])/(knots[i+order-1]-knots[i]) ) <<"  points["<<i+1<<"] = "<< points[i+1]<<std::endl;

    		std::cerr<<"  nouveau point         "<<((knots[i+order-1]-u)/(knots[i+order-1]-knots[i] )*points[i]+ ((u-knots[i])/(knots[i+order-1]-knots[i]))*points[i+1] )<<std::endl;

    		std::cerr<<"  nouds ["<< i+order-1<<"] = "<<(knots[i+order-1])<< "   noeud["<<i<<"] = "<<knots[i]<<std::endl<<std::endl;
			 */


			newPoints.push_back((knots[i+order-1]-u)/(knots[i+order-1]-knots[i] )*points[i]+ ((u-knots[i])/(knots[i+order-1]-knots[i]))*points[i+1]);
		}
		int size =newPoints.size();
		//std::cerr<<" points[0] "<< points[0] <<std::endl;

		//std::cerr<<"newPoints.size()  =  "<< newPoints.size()<<std::endl;
		if(size==1){
			//	std::cerr<<" newPoints[0] "<< newPoints[0] <<std::endl;
			return newPoints[0];
		}
		size =(knots.size()-2);
		for(j=1;j<=size;j++){
			newKnots.push_back(knots[j]);
		}
		return blossom(newPoints,newKnots,order-1,u);
	}


};


MyGLLinesMesh::MyGLLinesMesh (const glm::mat4 transform, BSplinePatch *mPatch)
:mModelToSceneMatrix(transform), mCurveSize(mPatch->_curveSize) {
/*
	// control polygon points
	mMesh.insert(mMesh.begin(), mPatch->_points.begin(), mPatch->_points.end());

	// control polygon indices for lines segments
	for(unsigned int j = 0; j<mCurveSize; ++j ){
		for(unsigned int i = 0; i<mCurveSize-1; ++i ){
			mIndices.push_back(j*mCurveSize+ i);
			mIndices.push_back(j*mCurveSize+i+1);
		}
	}

	for(unsigned int j = 0; j<mCurveSize; ++j ){
		for(unsigned int i = 0; i<mCurveSize-1; ++i ){
			mIndices.push_back(i*mCurveSize+ j);
			mIndices.push_back((i+1)*mCurveSize+j);
		}
	}



	/// @todo TP GEOM
	// inserer les points correspondants aux courbe BSpline des directrices en u et v
	// de meme pour les indices des lignes


	//test ici pour affichage
	int k;
	int subdiv =30;
	float v ,u;
	int l =mCurveSize*mCurveSize;
	for(k =0; k<mCurveSize;k++){

		for(int i=0;i<=subdiv;i++){
			//(i/subdiv) * (Umax -Umin) +Umin
			u = (float(i)/float(subdiv))* (mPatch->_knots[mPatch->_knotSize - mPatch->_order] - mPatch->_knots[mPatch->_degree])+mPatch->_knots[mPatch->_degree];

			mMesh.push_back(BSplineCurveEvaluator::evaluate(mPatch->directricePointsU(k),mPatch->_knots,mPatch->_order,u));
		}
		int j;
		for(j =0;j<=subdiv;j++){
			mIndices.push_back(l+j);
		}
		l=l+j;
	}


	for(k =0; k<mCurveSize;k++){

		for(int i=0;i<=subdiv;i++){
			//(i/subdiv) * (Umax -Umin) +Umin
			v= (float(i)/float(subdiv))* (mPatch->_knots[mPatch->_knotSize - mPatch->_order] - mPatch->_knots[mPatch->_degree])+mPatch->_knots[mPatch->_degree];

			mMesh.push_back(BSplineCurveEvaluator::evaluate(mPatch->directricePointsV(k),mPatch->_knots,mPatch->_order,v));
		}
		int j;
		for(j =0;j<=subdiv;j++){
			mIndices.push_back(l+j);
		}
		l=l+j;
	}
	std::cerr<<"k =   " << k <<std::endl;
	std::cerr<<"l =   " << l <<std::endl;
	std::cerr<<"curvesize  " << mCurveSize <<std::endl;
*/


}


void MyGLLinesMesh::compileGL() {
	glAssert( glGenVertexArrays(1, &mVertexArrayObject) );
	glAssert( glGenBuffers(2, mVertexBufferObjects) );
	glAssert( glBindVertexArray(mVertexArrayObject) );
	glAssert( glBindBuffer(GL_ARRAY_BUFFER, mVertexBufferObjects[VBO_VERTICES]) );

	glAssert( glBufferData(GL_ARRAY_BUFFER, mMesh.size() * sizeof(glm::vec3),  &(mMesh[0]), GL_STATIC_DRAW) );
	GLuint stride = sizeof(glm::vec3);
	GLboolean normalized = GL_FALSE;
	GLenum type = GL_FLOAT;
	// position
	GLuint index = 0;
	GLint size = 3;
	GLvoid *pointer = BUFFER_OFFSET(0);
	glAssert( glVertexAttribPointer(index, size, type, normalized, stride, pointer) );
	glAssert( glEnableVertexAttribArray(index) );

	glAssert( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVertexBufferObjects[VBO_INDICES]) );
	glAssert( glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndices.size() * sizeof(int),  &(mIndices[0]), GL_STATIC_DRAW) );
	glAssert( glBindVertexArray(0) );

}

void MyGLLinesMesh::destroyGL(){
	glAssert(glDeleteVertexArrays(1, &mVertexArrayObject));
	mVertexArrayObject = 0;
	glAssert(glDeleteBuffers(2, mVertexBufferObjects));
	mVertexBufferObjects[0] = 0;
	mVertexBufferObjects[1] = 1;
}


void MyGLLinesMesh::drawGL(const glm::mat4x4 &sceneToViewMatrix, const glm::mat4x4 &projectionMatrix, int transformLocation[4], int colorLocation){
	glm::mat4x4 localprojectionMatrix = projectionMatrix;
	glm::mat4x4 localmodelViewMatrix = sceneToViewMatrix*mModelToSceneMatrix;
	glm::mat4x4 normalMatrix = glm::transpose(glm::inverse(localmodelViewMatrix));
	glm::mat4x4 MVP = localprojectionMatrix * localmodelViewMatrix;
	glAssert( glUniformMatrix4fv( transformLocation[0], 1, GL_FALSE, glm::value_ptr(localmodelViewMatrix)) );
	glAssert( glUniformMatrix4fv( transformLocation[1], 1, GL_FALSE, glm::value_ptr(localprojectionMatrix)));
	glAssert( glUniformMatrix4fv( transformLocation[2], 1, GL_FALSE, glm::value_ptr(normalMatrix)));
	glAssert( glUniformMatrix4fv( transformLocation[3], 1, GL_FALSE, glm::value_ptr(MVP)));

	glAssert( glBindVertexArray(mVertexArrayObject) );
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glEnable(GL_LINE_SMOOTH);
	glm::vec4 color = glm::vec4(1,1,1,.5);
	glAssert( glUniform4fv( colorLocation, 1, glm::value_ptr(color)) );

	glLineWidth(1.5f);


	glAssert( glDrawElements(GL_LINES, 2*2*mCurveSize*(mCurveSize-1), GL_UNSIGNED_INT, NULL) );//NULL offset p/r au debut


	/// @todo TP1 GEOM
	// dessiner les courbes de control en u en rouge
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	color = glm::vec4(1,0,0,1);
	glLineWidth(3.f);
	glAssert( glUniform4fv( colorLocation, 1, glm::value_ptr(color)) );

	int subdiv=30;
	int j ,i;
	for(i =0; i<mCurveSize;i++){
		glAssert( glDrawElements(GL_LINE_STRIP, subdiv+1, GL_UNSIGNED_INT, BUFFER_OFFSET((2*2*mCurveSize*(mCurveSize-1)+((subdiv+1)*i))*sizeof(int))));//changer le nbre d'elmt indice et le NULL
	}

	// et celle en v en bleu
	color = glm::vec4(0,0,1,1);
	glAssert( glUniform4fv( colorLocation, 1, glm::value_ptr(color)) );

	for(j=0; j<mCurveSize;j++){
		glAssert( glDrawElements(GL_LINE_STRIP, subdiv+1, GL_UNSIGNED_INT, BUFFER_OFFSET((2*2*mCurveSize*(mCurveSize-1)+(((subdiv+1)*i)+(subdiv+1)*j))*sizeof(int))));//changer le nbre d'elmt indice et le NULL
	}


	glLineWidth(1.f);
	glDisable(GL_BLEND);
}



BSplinePatch::BSplinePatch( int degree, int knotVectorType, const std::vector<glm::vec3> &points, int curveSize)
:  _degree(degree)
,  _order(degree+1)
,  _curveSize(curveSize)
, _n(_curveSize-1)
, _knotSize(_order+_curveSize)
{


	_points.insert(_points.begin(), points.begin(), points.end());

	/// @todo TP GEOM
	int i,j;
	float pas = 1.0;
	//std::cerr<<sizeof(_knots)/sizeof(float)<<std::endl;
	//std::cerr<<_knotSize<<std::endl;
	//  std::cerr<<"knotsize  " <<  _knotSize<<std::endl;
	//  std::cerr<<_knotSize<<std::endl;
	std::cerr<<_order<<std::endl;


	if(knotVectorType == Uniform){
		//  std::cerr<<"knots Uniform"<<std::endl;
		for(i=0;i<_knotSize;i++){
			_knots.push_back(i*pas);
			//   std::cerr<<_knots[i]<<std::endl;
		}
	}
	else if(knotVectorType == OpenUniform){
		j=0;
		//  std::cerr<<"knots OpenUniform"<<std::endl;
		for(i=0;i<_knotSize;i++){
			if(i<_degree){
				_knots.push_back(j*pas);
			}
			else if (i <= _knotSize-_degree){
				j++;
				_knots.push_back(j*pas);

			}
			else {
				_knots.push_back(j*pas);
			}
			//	   std::cerr<<"noeud["<<i<<"] = "<<_knots[i]<<std::endl;
		}
	}
	// std::cerr<<_curveSize<<std::endl;
	//directricePointsU(1);
	//directricePointsV(3);
}

std::vector<glm::vec3> BSplinePatch::directricePointsV(int baseIndex) const{

	std::cerr<<"V"<<std::endl;
	/// @todo TP2 G
	int i;
	std::vector<glm::vec3> directrice;
	for(i=0;i<_curveSize;i++){
		directrice.push_back(_points[baseIndex+_curveSize*i]);
		std::cerr<<baseIndex+_curveSize*i<<std::endl;
	}
	return directrice;

}


std::vector<glm::vec3> BSplinePatch::directricePointsU(int baseIndex) const{
	std::cerr<<"U"<<std::endl;
	/// @todo TP2 G
	int i;
	std::vector<glm::vec3> directrice;
	for(i=0;i<_curveSize;i++){
		directrice.push_back(_points[baseIndex*_curveSize +i]);
		std::cerr<< baseIndex+i<<std::endl;
	}
	//std::cerr<<"fin U"<<std::endl;
	return directrice;

}
glm::vec3 BSplinePatch::evalSurface(float u, float v) const {
	/// @todo TP GEOM
	BSplinePatch *mPatch;
	int k;
	int subdiv =30;
	float vv ,uu;
	//int l =mCurveSize*mCurveSize;
	for(k =0; k<mPatch->_curveSize;k++){

		//(i/subdiv) * (Umax -Umin) +Umin
		uu = u * (mPatch->_knots[mPatch->_knotSize - mPatch->_order] - mPatch->_knots[mPatch->_degree])+mPatch->_knots[mPatch->_degree];

		glm::vec3 vert = (BSplineCurveEvaluator::evaluate(mPatch->directricePointsU(k),mPatch->_knots,mPatch->_order,uu));
	}
	int j;
	for(j =0;j<=subdiv;j++){
		//	mIndices.push_back(l+j);
	}
	//	l=l+j;




	return glm::vec3(1.f, 0.f, 0.f);
}

glm::vec3 BSplinePatch::evalSurfaceNormal(float u, float v) const {

	BSplinePatch *mPatch;
	glm::vec3 tanU, tanV;
	int k;
	for(k =0; k<mPatch->_curveSize;k++){
		tanU=(BSplineCurveEvaluator::evaluateTan(mPatch->directricePointsU(k),mPatch->_knots,mPatch->_order,u));

		tanV=(BSplineCurveEvaluator::evaluateTan(mPatch->directricePointsV(k),mPatch->_knots,mPatch->_order,v));


	}
	/// @todo TP GEOM
	return glm::vec3(1.f, 0.f, 0.f);
}


loaders::Mesh::Vertex ParametricBSplinePatch::evalSurface(float u, float v) const {
	assert(u >= 0.f); assert(u <= 1.f);
	assert(v >= 0.f); assert(v <= 1.f);

	Vertex vert;
	vert.texcoord = glm::vec2(u, v);
	/// @todo TP GEOM
	// utiliser mPatch->evalSurface et evalSurfaceNormal

	return ParametricMesh::evalSurface(u,v);
}

}





